﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Series;
using DetectingKinectSencor._40_Tools;
using OxyPlot.Annotations;
using DetectingKinectSencor._10_ColorStream;
using System.IO;
using System.Windows.Media.Imaging;
using System.Drawing;

namespace DetectingKinectSencor._50_Evaluation.ViewModels.DetailedEvalaution
{
    public class GesturesViewModelDetailed : Notifier
    {
        // Internal variable in which the model is saved in this class
        private PlotModel plotModel;
        private PlotModel plotModelTotalAmount;
        private PlotModel plotModelPercentage;

        private PlotModel tempPlotModelDataTime;
        private PlotModel tempPlotModelTotalAmount;
        private PlotModel tempPlotModelPercentage;

        private LineSeries timepointSerieOpenGesturesList;
        private LineSeries timepointSerieHandsBelowWaistlineList;
        private LineSeries timepointSerieHandsBelowWaistlineListAggregated;
        private LineSeries timepointSerieOpenGestureListAggregated;

        private DateTime captureTimeLastSequence;
        private int secondsForFileName;
        private int minutesForFileName;
        private List<TimeSpanAggregated> timeSpanAggregated;

        // Defines a timespan of 30 seconds
        private TimeSpan interval = new TimeSpan(0,0,60);
        private TimeSpan actualInterval;
        private TimeSpanAggregated timeSpan;
        private TimeSpan actualIntervalUpperBound;


        // Important the UI needs to bind the Model to MovingPlotModel!!!
        public PlotModel GesturesPlotModel
        {
            get { return plotModel; }
            set { plotModel = value; RaisePropertyChanged(() => GesturesPlotModel); }
        }

        public PlotModel GesturesPlotModelTotalAmount
        {
            get { return plotModelTotalAmount; }
            set { plotModelTotalAmount = value; RaisePropertyChanged(() => GesturesPlotModelTotalAmount); }
        }

        public PlotModel GesturesPlotModelPercentage
        {
            get { return plotModelPercentage; }
            set { plotModelPercentage = value; RaisePropertyChanged(() => GesturesPlotModelPercentage); }
        }


        /// <summary>
        /// Used in the detailed evaluation
        /// </summary>
        /// <param name="lastElementCaptureTime"></param>
        /// <param name="snapshotManager"></param>
        public GesturesViewModelDetailed(DateTime lastElementCaptureTime, SnapshotManager snapshotManager)
        {


            tempPlotModelDataTime = new PlotModel();
            tempPlotModelTotalAmount = new PlotModel();
            tempPlotModelPercentage = new PlotModel();


            SetUpModelDetailed();
            LoadDataDetailed(lastElementCaptureTime, snapshotManager);

        }

       
        private void SetUpModelDetailed()
        {
            // Define the legend for the date/time graph

            tempPlotModelDataTime.LegendTitle = "Legend";
            tempPlotModelDataTime.LegendOrientation = LegendOrientation.Horizontal;
            tempPlotModelDataTime.LegendPlacement = LegendPlacement.Outside;
            tempPlotModelDataTime.LegendPosition = LegendPosition.TopRight;
            tempPlotModelDataTime.LegendBackground = OxyColor.FromAColor(200, OxyColors.White);
            tempPlotModelDataTime.LegendBorder = OxyColors.Black;
            tempPlotModelDataTime.LegendFontSize = DesignOfGraphs.fontSizeDetailed;

            TimeSpanAxis timeLineAxis = new TimeSpanAxis(AxisPosition.Bottom, "Timeline (mm:ss)", "mm:ss")
            {
                MajorGridlineStyle = LineStyle.Solid,
                MinorGridlineStyle = LineStyle.Dot,
                FontSize = DesignOfGraphs.fontSizeDetailed
            };

            // Add the axes to the plot model
            tempPlotModelDataTime.Axes.Add(timeLineAxis);

            LinearAxis valueAxis = new LinearAxis(AxisPosition.Left, 0, 3, "Detections\nidentifier")
            {
                MajorGridlineStyle = LineStyle.Solid,
                MinorGridlineStyle = LineStyle.Dot,
                MinorStep = 0.5,
                MajorStep = 1,
                FontSize = DesignOfGraphs.fontSizeDetailed
            };
            tempPlotModelDataTime.Axes.Add(valueAxis);




            // Define the legend for the bar graph

            tempPlotModelTotalAmount.LegendTitle = "Legend";
            tempPlotModelTotalAmount.LegendOrientation = LegendOrientation.Horizontal;
            tempPlotModelTotalAmount.LegendPlacement = LegendPlacement.Outside;
            tempPlotModelTotalAmount.LegendPosition = LegendPosition.BottomCenter;
            tempPlotModelTotalAmount.LegendBackground = OxyColor.FromAColor(200, OxyColors.White);
            tempPlotModelTotalAmount.LegendBorder = OxyColors.Black;
            tempPlotModelTotalAmount.LegendFontSize = DesignOfGraphs.fontSizeDetailed;


            // Axes for the bar graph
            CategoryAxis categoryAxis = new CategoryAxis();

            //            categoryAxis.MinorStep = 1;
            categoryAxis.Labels.Add("Total Amount");
            categoryAxis.FontSize = DesignOfGraphs.fontSizeDetailed;

            tempPlotModelTotalAmount.Axes.Add(categoryAxis);
            tempPlotModelTotalAmount.LegendFontSize = DesignOfGraphs.fontSizeDetailed;





            // Define the legend for the percentage graph

            tempPlotModelPercentage.LegendTitle = "Legend";
            tempPlotModelPercentage.LegendOrientation = LegendOrientation.Horizontal;
            tempPlotModelPercentage.LegendPlacement = LegendPlacement.Outside;
            tempPlotModelPercentage.LegendPosition = LegendPosition.TopRight;
            tempPlotModelPercentage.LegendBackground = OxyColor.FromAColor(200, OxyColors.White);
            tempPlotModelPercentage.LegendBorder = OxyColors.Black;
            tempPlotModelPercentage.LegendFontSize = DesignOfGraphs.fontSizeDetailed;

            TimeSpanAxis timeLineAxisPercentage = new TimeSpanAxis(AxisPosition.Bottom, "Timeline (mm:ss)", "mm:ss")
            {
                MajorGridlineStyle = LineStyle.Solid,
                MinorGridlineStyle = LineStyle.Dot,
                MajorStep = interval.TotalSeconds,
                Angle = -50,
                AxisTitleDistance = 12,
                FontSize = DesignOfGraphs.fontSizeDetailed
            };

            // Add the axes to the plot model
            tempPlotModelPercentage.Axes.Add(timeLineAxisPercentage);


            LinearAxis valueAxisPercentage = new LinearAxis(AxisPosition.Left, "Detections in\none minute")
            {
                MajorGridlineStyle = LineStyle.Solid,
                MinorGridlineStyle = LineStyle.Dot,
                FontSize = DesignOfGraphs.fontSizeDetailed
            };

            // Add the axes to the plot model
            tempPlotModelPercentage.Axes.Add(valueAxisPercentage);
        }


        


        public void LoadDataDetailed(DateTime captureTime, SnapshotManager snapshotManager)
        {
            // Get the list with the viewingDirectionData
            OpenGestureData openGestureList = EvaluationData.openGestureData;
            HandsBelowWaistlineData handsBelowWaistlineList = EvaluationData.handsBelowWaistlineData;
            TimeSpan elapsedTime;

            this.captureTimeLastSequence = captureTime;



            // Defines the data/time graph

            timepointSerieOpenGesturesList = new LineSeries
            {
                StrokeThickness = 2,
                MarkerSize = 3,
                MarkerStroke = Graph.colors[1],
                MarkerType = Graph.markerTypes[1],
                CanTrackerInterpolatePoints = false,
                Title = string.Format("Open Gestures"),
                Smooth = false,
            };

            timepointSerieHandsBelowWaistlineList = new LineSeries
            {
                StrokeThickness = 2,
                MarkerSize = 3,
                MarkerStroke = Graph.colors[2],
                MarkerType = Graph.markerTypes[2],
                CanTrackerInterpolatePoints = false,
                Title = string.Format("Hands below waistline"),
                Smooth = false,
            };




            // Defines the bars of total Amount graph

            ColumnSeries columnSeriesOpenGestures = new ColumnSeries
            {

                LabelFormatString = "{0}",
                StrokeThickness = 1,
                Title = "Open gestures",
                FillColor = Graph.colors[1]
            };
            tempPlotModelTotalAmount.Series.Add(columnSeriesOpenGestures);


            ColumnSeries columnSeriesHandsBelowWaistline = new ColumnSeries
            {
                LabelFormatString = "{0}",
                FillColor = Graph.colors[2],
                StrokeThickness = 1,
                Title = "Hands below waistline",
            };

            tempPlotModelTotalAmount.Series.Add(columnSeriesHandsBelowWaistline);





            // Defines the percentage graph

            timepointSerieHandsBelowWaistlineListAggregated = new StairStepSeries
            {
                CanTrackerInterpolatePoints = false,
                VerticalLineStyle = LineStyle.Dash,
                Title = string.Format("Hands below waistline"),
                Smooth = false,
                Color = OxyColors.Blue
            };

            

            timepointSerieOpenGestureListAggregated = new StairStepSeries
            {
                CanTrackerInterpolatePoints = false,
                VerticalLineStyle = LineStyle.Dash,
                Title = string.Format("Open gestures"),
                Smooth = false,
                Color = OxyColors.Orange
            };



            // Add the data points openGesture

            foreach (CaptureTime openGesture in openGestureList.trackingSequences)
            {
                if (captureTimeLastSequence == openGesture.captureTime)
                {
                    timeSpanAggregated = new List<TimeSpanAggregated>();

                    // The actualInterval starts at 0 seconds
                    actualInterval = new TimeSpan(0, 0, 0);

                    // The upper bound starts at 30 seconds
                    actualIntervalUpperBound = interval;

                    // The first interval is 0-30
                    timeSpan = new TimeSpanAggregated(new TimeSpan(0, 0, 0), 0);


                    for (int i = 0; i < openGesture.timePoints.Count; i++)
                    {
                        // It is more intuitiv to see the actual elapsed time from the beginning then the actual time 
                        elapsedTime = openGesture.timePoints[i].timePoint - captureTimeLastSequence;
                        timepointSerieOpenGesturesList.Points.Add(new DataPoint(
                                                                      TimeSpanAxis.ToDouble(elapsedTime),
                                                                      1));

                        // Add the timepoints which are in the interval to the list and calculate the percentage later
                        if (elapsedTime >= actualInterval && elapsedTime <= actualIntervalUpperBound)
                        {
                            // Total amount open gestures
                            timeSpan.totalAmount++;
                        }
                        else
                        {
                            // Save the current timespan
                            timeSpanAggregated.Add(timeSpan);

                            // Define the lower bound of the interval
                            actualInterval += interval;

                            // Define the upper bound of the interval
                            actualIntervalUpperBound += interval;

                            // Timespan object for the new interval
                            timeSpan = new TimeSpanAggregated(actualInterval, 0);

                            // decrement the indexer that the data point is checked against the next interval
                            i--;
                        }
                    
                    }

                    // Checks if the last timespan has already been added or not
                    if (timeSpanAggregated.Count != 0)
                    {
                        if (!(timeSpanAggregated[timeSpanAggregated.Count - 1].Equals(timeSpan)))
                        {
                            timeSpanAggregated.Add(timeSpan);
                        }
                    }


                    // Define start interval
                    TimeSpan intervalPercentage = new TimeSpan(0, 0, 0);

                    for (int i = 0; i < timeSpanAggregated.Count; i++)
                    {

                        timepointSerieOpenGestureListAggregated.Points.Add(new DataPoint(
                                                                                TimeSpanAxis.ToDouble(intervalPercentage),
                                                                                timeSpanAggregated[i].totalAmount));

                        // When using the step graph, the peek needs to be at the starting point 0,
                        // because the first interval is 0-30. This is why the saved interval in 
                        // timeSpanAggregated[i].timespan can not be used, because then would the first 
                        // peek be at 30! and the whole graph would be moved to the right.
                        intervalPercentage += interval;
                    }

                    // Add a final point which closes the interval
                    timepointSerieOpenGestureListAggregated.Points.Add(new DataPoint(
                                                                                TimeSpanAxis.ToDouble(intervalPercentage),
                                                                                0));


                    // Add the time point to the date/time graph
                    tempPlotModelDataTime.Series.Add(timepointSerieOpenGesturesList);

                    // ad the total Amount to the bar graph
                    columnSeriesOpenGestures.Items.Add(new ColumnItem(openGesture.timePoints.Count));

                    tempPlotModelPercentage.Series.Add(timepointSerieOpenGestureListAggregated);

                    break;
                }
            }



            // Add the data points handsBelowWaistline
            foreach (CaptureTime handsBelowWaistline in handsBelowWaistlineList.trackingSequences)
            {
                if (captureTimeLastSequence == handsBelowWaistline.captureTime)
                {
                    timeSpanAggregated = new List<TimeSpanAggregated>();

                    // The actualInterval starts at 0 seconds
                    actualInterval = new TimeSpan(0, 0, 0);

                    // The upper bound starts at 30 seconds
                    actualIntervalUpperBound = interval;

                    // The first interval is 0-30
                    timeSpan = new TimeSpanAggregated(new TimeSpan(0, 0, 0), 0);


                    for (int i = 0; i < handsBelowWaistline.timePoints.Count; i++)
                    {
                        // It is more intuitiv to see the actual elapsed time from the beginning then the actual time 
                        elapsedTime = handsBelowWaistline.timePoints[i].timePoint - captureTimeLastSequence;

                        timepointSerieHandsBelowWaistlineList.Points.Add(new DataPoint(
                                                                             TimeSpanAxis.ToDouble(elapsedTime),
                                                                             2));
                        
                        
                        // Add the timepoints which are in the interval to the list and calculate the percentage later
                        if (elapsedTime >= actualInterval && elapsedTime <= actualIntervalUpperBound)
                        {
                            // Total amount for interval hands below waistline
                            timeSpan.totalAmount++;
                        }
                        else
                        {
                            // Save the current timespan
                            timeSpanAggregated.Add(timeSpan);

                            // Define the lower bound of the interval
                            actualInterval += interval;

                            // Define the upper bound of the interval
                            actualIntervalUpperBound += interval;

                            // Timespan object for the new interval
                            timeSpan = new TimeSpanAggregated(actualInterval, 0);

                            // decrement the indexer that the data point is checked against the next interval
                            i--;
                        }
                    }

                    // Checks if the last timespan has already been added or not
                    if (timeSpanAggregated.Count != 0)
                    {
                        if (!(timeSpanAggregated[timeSpanAggregated.Count - 1].Equals(timeSpan)))
                        {
                            timeSpanAggregated.Add(timeSpan);
                        }
                    }

                    // Define start interval
                    TimeSpan intervalPercentage = new TimeSpan(0,0,0);

                    for (int i = 0; i < timeSpanAggregated.Count; i++)
                    {

                        timepointSerieHandsBelowWaistlineListAggregated.Points.Add(new DataPoint(
                                                                                TimeSpanAxis.ToDouble(intervalPercentage),
                                                                                timeSpanAggregated[i].totalAmount));
                        
                        // When using the step graph, the peek needs to be at the starting point 0,
                        // because the first interval is 0-30. This is why the saved interval in 
                        // timeSpanAggregated[i].timespan can not be used, because then would the first 
                        // peek be at 30! and the whole graph would be moved to the right.
                        intervalPercentage += interval;
                    }
                    
                    // Add a final point which closes the interval
                    timepointSerieHandsBelowWaistlineListAggregated.Points.Add(new DataPoint(
                                                                                TimeSpanAxis.ToDouble(intervalPercentage),
                                                                                0));


                    // Add the time point to the date/time graph
                    tempPlotModelDataTime.Series.Add(timepointSerieHandsBelowWaistlineList);


                    // ad the total Amount to the bar graph
                    columnSeriesHandsBelowWaistline.Items.Add(new ColumnItem(handsBelowWaistline.timePoints.Count));



                    tempPlotModelPercentage.Series.Add(timepointSerieHandsBelowWaistlineListAggregated);

                    break;
                }
            }


            //Saved the tempPlotModel to the plotModel than is the OnPropertyChanged executed and the UI is updated
            GesturesPlotModel = tempPlotModelDataTime;
            GesturesPlotModelTotalAmount = tempPlotModelTotalAmount;
            GesturesPlotModelPercentage = tempPlotModelPercentage;








            // Event handler for the mous click open gesture
            timepointSerieHandsBelowWaistlineList.MouseDown += (s, e) =>
            {
                // only handle the left mouse button (right button can still be used to pan)
                if (e.ChangedButton == OxyMouseButton.Left)
                {
                    int indexOfNearestPoint = (int)Math.Round(e.HitTestResult.Index);

                    
                    //var nearestPoint = timepointSerieOpenGesturesList.Transform(timepointSerieOpenGesturesList.Points[indexOfNearestPoint]);
                    //Console.WriteLine("neares point: " + nearestPoint);


                    foreach (CaptureTime handsBelowWaistline in handsBelowWaistlineList.trackingSequences)
                    {
                        if (captureTimeLastSequence == handsBelowWaistline.captureTime)
                        {
                            // Calculate the timePoint for the clicked point on the graph
                            double timePoint = timepointSerieHandsBelowWaistlineList.Points[indexOfNearestPoint].X;

                            for (int i = 0; i < handsBelowWaistline.timePoints.Count; i++)
                            {
                                // calculate the timePoint for the HandsBelowWaistlineList timePoint
                                double elapsedTimeTemp = TimeSpanAxis.ToDouble(handsBelowWaistline.timePoints[i].timePoint - captureTimeLastSequence);

                                if (elapsedTimeTemp == timePoint)
                                {
                                    DateTime tempTimePoint = handsBelowWaistline.timePoints[i].timePoint;

                                    minutesForFileName = tempTimePoint.Minute;
                                    secondsForFileName = tempTimePoint.Second;


                                    for (int j = 0; j <= XMLproperties.secondsTillSnapshot; j++)
                                    {

                                        string filename = "Snapshots\\" +
                                                          tempTimePoint.Day +
                                                          "_" +
                                                          tempTimePoint.Month +
                                                          "_" +
                                                          tempTimePoint.Year +
                                                          "_" +
                                                          tempTimePoint.Hour +
                                                          "_" +
                                                          minutesForFileName +
                                                          "_" +
                                                          secondsForFileName +
                                                          ".jpg";

                                        if (File.Exists(filename))
                                        {

                                            Stream imageStreamSource = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
                                            JpegBitmapDecoder decoder = new JpegBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
                                            BitmapSource bitmapSource = decoder.Frames[0];

                                            WriteableBitmap wbitmap = new WriteableBitmap(bitmapSource);

                                            snapshotManager.update(wbitmap);
                                            break;
                                        }

                                        if ((secondsForFileName) > 60)
                                        {
                                            minutesForFileName++;
                                            secondsForFileName = 0;
                                        }
                                        else
                                        {
                                            secondsForFileName++;
                                        }
                                    }
                                    break;
                                }


                            }
                            break;
                        }
                    }
                }
            };


            // Event handler for the mous click hands below the waistline
            timepointSerieOpenGesturesList.MouseDown += (s, e) =>
            {
                // only handle the left mouse button (right button can still be used to pan)
                if (e.ChangedButton == OxyMouseButton.Left)
                {
                    int indexOfNearestPoint = (int)Math.Round(e.HitTestResult.Index);


                    //var nearestPoint = timepointSerieOpenGesturesList.Transform(timepointSerieOpenGesturesList.Points[indexOfNearestPoint]);
                    //Console.WriteLine("neares point: " + nearestPoint);


                    foreach (CaptureTime openGesture in openGestureList.trackingSequences)
                    {
                        if (captureTimeLastSequence == openGesture.captureTime)
                        {
                            // Calculate the timePoint for the clicked point on the graph
                            double timePoint = timepointSerieOpenGesturesList.Points[indexOfNearestPoint].X;

                            for (int i = 0; i < openGesture.timePoints.Count; i++)
                            {
                                // calculate the timePoint for the openGesture timePoint
                                double elapsedTimeTemp = TimeSpanAxis.ToDouble(openGesture.timePoints[i].timePoint - captureTimeLastSequence);

                                if (elapsedTimeTemp == timePoint)
                                {
                                    DateTime tempTimePoint = openGesture.timePoints[i].timePoint;

                                    minutesForFileName = tempTimePoint.Minute;
                                    secondsForFileName = tempTimePoint.Second;


                                    for (int j = 0; j <= XMLproperties.secondsTillSnapshot; j++)
                                    {

                                        string filename = "Snapshots\\" +
                                                          tempTimePoint.Day +
                                                          "_" +
                                                          tempTimePoint.Month +
                                                          "_" +
                                                          tempTimePoint.Year +
                                                          "_" +
                                                          tempTimePoint.Hour +
                                                          "_" +
                                                          minutesForFileName +
                                                          "_" +
                                                          secondsForFileName +
                                                          ".jpg";

                                        if (File.Exists(filename))
                                        {

                                            Stream imageStreamSource = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
                                            JpegBitmapDecoder decoder = new JpegBitmapDecoder(imageStreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
                                            BitmapSource bitmapSource = decoder.Frames[0];

                                            WriteableBitmap wbitmap = new WriteableBitmap(bitmapSource);

                                            snapshotManager.update(wbitmap);
                                            break;
                                        }

                                        if ((secondsForFileName) > 60)
                                        {
                                            minutesForFileName++;
                                            secondsForFileName = 0;
                                        }
                                        else
                                        {
                                            secondsForFileName++;
                                        }
                                    }
                                    break;
                                }


                            }
                            break;
                        }
                    }
                }
            };


        }
    }
}





